﻿using System.Security.Cryptography;
using System.Text;

namespace System.NETool;

/// <summary>
/// RSA算法实现（公钥加密私钥解密，私钥加密公钥解密）
/// 密钥长度为1024，加密数据长度不限制
/// https://www.cnblogs.com/kuaileguaiwu/archive/2009/04/21/1440471.html
/// </summary>
public static class RSA
{
    /// <summary>
    /// 通过公钥加密
    /// </summary>
    /// <param name="content">待加密字符串</param>
    /// <param name="xmlPublicKey">公钥</param>
    /// <returns>加密结果</returns>
    public static byte[] EncryptByPublicKey(string content, string xmlPublicKey)
    {
        //取得公钥参数
        using var rsa = new RSACryptoServiceProvider();
        rsa.FromXmlString(xmlPublicKey);
        var parameters = rsa.ExportParameters(false);
        var keyN = parameters.Modulus;
        var keyE = parameters.Exponent;
        //大整数N
        var biN = new BigInteger(keyN);
        //公钥大素数
        var biE = new BigInteger(keyE);
        //加密
        return EncryptString(content, biE, biN);
    }

    /// <summary>
    /// 通过公钥加密
    /// </summary>
    /// <param name="dataStr">待加密字符串</param>
    /// <param name="n">大整数n</param>
    /// <param name="e">公钥</param>
    /// <returns>加密结果</returns>
    public static byte[] EncryptByPublicKey(string dataStr, string n, string e)
    {
        //大整数N
        var biN = new BigInteger(n, 16);
        //公钥大素数
        var biE = new BigInteger(e, 16);
        //加密
        return EncryptString(dataStr, biE, biN);
    }

    /// <summary>
    /// 通过私钥解密
    /// </summary>
    /// <param name="content">待解密字符数组</param>
    /// <param name="xmlPrivateKey">私钥</param>
    /// <returns>解密结果</returns>
    public static string DecryptByPrivateKey(byte[] content, string xmlPrivateKey)
    {
        //取得私钥参数
        using var rsa = new RSACryptoServiceProvider();
        rsa.FromXmlString(xmlPrivateKey);
        var parameters = rsa.ExportParameters(true);
        var keyN = parameters.Modulus;
        var keyD = parameters.D;
        //大整数N
        var biN = new BigInteger(keyN);
        //私钥大素数
        var biD = new BigInteger(keyD);
        //解密
        return DecryptBytes(content, biD, biN);
    }

    /// <summary>
    /// 通过私钥解密
    /// </summary>
    /// <param name="content">待解密字符数组</param>
    /// <param name="n">大整数n</param>
    /// <param name="d">私钥</param>
    /// <returns>解密结果</returns>
    public static string DecryptByPrivateKey(byte[] content, string n, string d)
    {
        //大整数N
        var biN = new BigInteger(n, 16);
        //私钥大素数
        var biD = new BigInteger(d, 16);
        //解密
        return DecryptBytes(content, biD, biN);
    }

    /// <summary>
    /// 通过私钥加密
    /// </summary>
    /// <param name="content">待加密字符串</param>
    /// <param name="xmlPrivateKey">私钥</param>
    /// <returns>加密结果</returns>
    public static byte[] EncryptByPrivateKey(string content, string xmlPrivateKey)
    {
        //取得私钥参数
        using var rsa = new RSACryptoServiceProvider();
        rsa.FromXmlString(xmlPrivateKey);
        var parameters = rsa.ExportParameters(true);
        var keyN = parameters.Modulus;
        var keyD = parameters.D;
        //大整数N
        var biN = new BigInteger(keyN);
        //私钥大素数
        var biD = new BigInteger(keyD);
        //加密
        return EncryptString(content, biD, biN);
    }

    /// <summary>
    /// 通过公钥解密
    /// </summary>
    /// <param name="content">待解密字符数组</param>
    /// <param name="xmlPublicKey">公钥</param>
    /// <returns>解密结果</returns>
    public static string DecryptByPublicKey(byte[] content, string xmlPublicKey)
    {
        //取得公钥参数
        using var rsa = new RSACryptoServiceProvider();
        rsa.FromXmlString(xmlPublicKey);
        var parameters = rsa.ExportParameters(false);
        var keyN = parameters.Modulus;
        var keyE = parameters.Exponent;
        //大整数N
        var biN = new BigInteger(keyN);
        //公钥大素数
        var biE = new BigInteger(keyE);
        //解密
        return DecryptBytes(content, biE, biN);
    }

    /// <summary>
    /// 加密字符串
    /// </summary>
    /// <param name="dataStr">待加密字符串</param>
    /// <param name="keyNum">密钥大素数</param>
    /// <param name="nNum">大整数N</param>
    /// <returns>加密结果</returns>
    private static byte[] EncryptString(string dataStr, BigInteger keyNum, BigInteger nNum)
    {
        var bytes = Encoding.UTF8.GetBytes(dataStr);
        var len = bytes.Length;
        var len1 = (len % 120) == 0 ? len / 120 : len / 120 + 1;
        var list = new List<byte>();
        for (int i = 0; i < len1; i++)
        {
            var blockLen = len >= 120 ? 120 : len;
            var oText = new byte[blockLen];
            Array.Copy(bytes, i * 120, oText, 0, blockLen);
            var biText = new BigInteger(oText);
            var biEnText = biText.modPow(keyNum, nNum);
            var resultStr = biEnText.ToHexString();
            if (resultStr.Length < 256)
            {
                while (resultStr.Length != 256)
                {
                    resultStr = "0" + resultStr;
                }
            }

            var returnBytes = new byte[128];
            for (int j = 0; j < returnBytes.Length; j++)
                returnBytes[j] = Convert.ToByte(resultStr.Substring(j * 2, 2), 16);

            list.AddRange(returnBytes);
            len -= blockLen;
        }

        return list.ToArray();
    }

    /// <summary>
    /// 解密字符数组
    /// </summary>
    /// <param name="dataBytes">待解密字符数组</param>
    /// <param name="keyNum">密钥大素数</param>
    /// <param name="nNum">大整数N</param>
    /// <returns>解密结果</returns>
    private static string DecryptBytes(byte[] dataBytes, BigInteger keyNum, BigInteger nNum)
    {
        int len = dataBytes.Length;
        var len1 = len % 128 == 0 ? len / 128 : len / 128 + 1;
        var temp = new List<byte>();
        for (int i = 0; i < len1; i++)
        {
            var blockLen = len >= 128 ? 128 : len;
            var oText = new byte[blockLen];
            Array.Copy(dataBytes, i * 128, oText, 0, blockLen);
            var biText = new BigInteger(oText);
            var biEnText = biText.modPow(keyNum, nNum);
            var bytes = biEnText.getBytes();
            temp.AddRange(bytes);
            len -= blockLen;
        }

        return Encoding.UTF8.GetString(temp.ToArray());
    }

    /// <summary>
    /// 生成密钥对（XML格式）
    /// 密钥的大小，必须从384位到16384位，增量为8
    /// </summary>
    /// <param name="keySize">密钥长度</param>
    /// <returns>密钥对</returns>
    public static RSAKey GenKey(int keySize = 1024)
    {
        using var rsa = new RSACryptoServiceProvider(keySize);
        return new RSAKey(rsa.ToXmlString(true), rsa.ToXmlString(false));
    }

    /// <summary>
    /// 生成密钥对
    /// RSA密码由三个整数组成，我们分别称之为n, e, d
    /// (n、d): 私钥，这个我们要私密保存
    /// (n、e): 公钥，可以对外公布
    /// </summary>
    /// <param name="n">n: 模数(Modulus)，私钥和公钥都包含有这个数</param>
    /// <param name="e">e: 公钥指数(publicExponent)，一般是固定值65537</param>
    /// <param name="d">d：私钥指数(privateExponent)</param>
    public static void GenKey(out string n, out string e, out string d)
    {
        byte[] pseudoPrime1 =
        [
            0x85, 0x84, 0x64, 0xFD, 0x70, 0x6A,
            0x9F, 0xF0, 0x94, 0x0C, 0x3E, 0x2C,
            0x74, 0x34, 0x05, 0xC9, 0x55, 0xB3,
            0x85, 0x32, 0x98, 0x71, 0xF9, 0x41,
            0x21, 0x5F, 0x02, 0x9E, 0xEA, 0x56,
            0x8D, 0x8C, 0x44, 0xCC, 0xEE, 0xEE,
            0x3D, 0x2C, 0x9D, 0x2C, 0x12, 0x41,
            0x1E, 0xF1, 0xC5, 0x32, 0xC3, 0xAA,
            0x31, 0x4A, 0x52, 0xD8, 0xE8, 0xAF,
            0x42, 0xF4, 0x72, 0xA1, 0x2A, 0x0D,
            0x97, 0xB1, 0x31, 0xB3
        ];

        byte[] pseudoPrime2 =
        [
            0x99, 0x98, 0xCA, 0xB8, 0x5E, 0xD7,
            0xE5, 0xDC, 0x28, 0x5C, 0x6F, 0x0E,
            0x15, 0x09, 0x59, 0x6E, 0x84, 0xF3,
            0x81, 0xCD, 0xDE, 0x42, 0xDC, 0x93,
            0xC2, 0x7A, 0x62, 0xAC, 0x6C, 0xAF,
            0xDE, 0x74, 0xE3, 0xCB, 0x60, 0x20,
            0x38, 0x9C, 0x21, 0xC3, 0xDC, 0xC8,
            0xA2, 0x4D, 0xC6, 0x2A, 0x35, 0x7F,
            0xF3, 0xA9, 0xE8, 0x1D, 0x7B, 0x2C,
            0x78, 0xFA, 0xB8, 0x02, 0x55, 0x80,
            0x9B, 0xC2, 0xA5, 0xCB
        ];

        var biP = new BigInteger(pseudoPrime1);
        var biQ = new BigInteger(pseudoPrime2);
        var biPq = (biP - 1) * (biQ - 1);
        var biN = biP * biQ;
        var rand = new Random();
        var biE = biPq.genCoPrime(512, rand);
        var biD = biE.modInverse(biPq);
        n = biN.ToHexString();
        e = biE.ToHexString();
        d = biD.ToHexString();
    }
}

/// <summary>
/// 公钥和私钥
/// </summary>
public readonly struct RSAKey(string privateKey, string publicKey)
{
    /// <summary>
    /// 私钥
    /// </summary>
    public string PrivateKey => privateKey;

    /// <summary>
    /// 公钥
    /// </summary>
    public string PublicKey => publicKey;

    public override string ToString() => $"PrivateKey: {PrivateKey}\r\nPublicKey: {PublicKey}";
}